﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AgCIM.Tools.SpatialEncoding.Beidou
{
    /// <summary>
    /// 北斗二维网格位置码
    /// </summary>
    public class BeidouSpatialEncodingPlanarCode
    {
        public List<string> Code { get; private set; }
        public double[] Coordinate { get; private set; }
        public List<double[]> Corners { get; private set; }
        private readonly char[] cellNum = "ABCDEFGHIJKLMNOPQRSTUV".ToCharArray();

        public BeidouSpatialEncodingPlanarCode()
        {
            Code = new List<string>();
            Corners = new List<double[]>();
        }

        public List<string> GetPlanarBeidouCode(double[] center, int level, bool isNeedLevel = true)
        {
            this.Code.Clear();
            this.Corners.Clear();
            this.Coordinate = new double[] { center[0], center[1] };

            // 定义计算方法数组，二维北斗网格最多划分十级lod
            Action[] computeMethods = {
            ComputeFirstLod,
            ComputeSecondLod,
            ComputeThirdLod,
            ComputeForthLod,
            ComputeFifthLod,
            ComputeSixthLod,
            ComputeSeventhLod,
            ComputeEighthLod,
            ComputeNinthLod,
            ComputeTenthLod
            };

            if (!isNeedLevel)
            {
                for (int i = 0; i < computeMethods.Length; i++)
                {
                    computeMethods[i].Invoke();
                }
            }
            else
            {
                for (int i = 0; i < level && i < computeMethods.Length; i++)
                {
                    computeMethods[i].Invoke();
                }
            }

            return this.Code;
        }        

        private void ComputeFirstLod()
        {
            if (this.Coordinate[1] >= 0)
            {
                this.Code.Add("N");
            }
            else
            {
                this.Code.Add("S");
            }

            // a表示纬度编码
            // b表示经度编码
            int latCode = (int)Math.Abs(this.Coordinate[1] / 4) + 1;
            int lngCode = (int)(this.Coordinate[0] / 6) + 31;
            if (this.Coordinate[0] < 0)
            {
                lngCode -= 1;
            }

            this.Code.Add($"{lngCode}{cellNum[latCode - 1]}");

            double cornerX = (lngCode - 1) * 6 - 180;
            double cornerY = (latCode - 1) * 4;

            this.Corners.Add(new double[] { cornerX, cornerY });
        }

        private void ComputeSecondLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 0.5, 0.5 }, new int[] { 12, 8 });

            this.Code.Add($"{codeArray[0]:X}{codeArray[1]}");
        }

        private void ComputeThirdLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 0.25, 10.0 / 60.0 }, new int[] { 2, 3 });
            int codePart = codeArray[0] + 2 * codeArray[1];

            this.Code.Add(codePart.ToString());
        }

        private void ComputeForthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            var codeArray = ComputeCornerAndCell(corner, new double[] { 1.0 / 60.0, 1.0 / 60.0 }, new int[] { 15, 10 });
            // 这里使用了十六进制表示法
            this.Code.Add($"{codeArray[0]:X}{codeArray[1]}");
        }

        private void ComputeFifthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 4.0 / 3600.0, 4.0 / 3600.0 }, new int[] { 15, 15 });
            // 这里使用了十六进制表示法
            this.Code.Add($"{codeArray[0]:X}{codeArray[1]:X}");
        }

        private void ComputeSixthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 2.0 / 3600.0, 2.0 / 3600.0 }, new int[] { 2, 2 });

            // 这里使用了计算公式 codeArray[0] + 2 * codeArray[1]
            this.Code.Add((codeArray[0] + 2 * codeArray[1]).ToString());
        }

        private void ComputeSeventhLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 1.0 / (4 * 3600.0), 1.0 / (4 * 3600.0) }, new int[] { 8, 8 });
            this.Code.Add($"{codeArray[0]}{codeArray[1]}");
        }

        private void ComputeEighthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 1.0 / (32 * 3600.0), 1.0 / (32 * 3600.0) }, new int[] { 8, 8 });
            this.Code.Add($"{codeArray[0]}{codeArray[1]}");
        }

        private void ComputeNinthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 1.0 / (256 * 3600.0), 1.0 / (256 * 3600.0) }, new int[] { 8, 8 });
            this.Code.Add($"{codeArray[0]}{codeArray[1]}");
        }

        private void ComputeTenthLod()
        {
            double[] corner = this.Corners[this.Corners.Count - 1];
            int[] codeArray = ComputeCornerAndCell(corner, new double[] { 1.0 / (2048 * 3600.0), 1.0 / (2048 * 3600.0) }, new int[] { 8, 8 });
            this.Code.Add($"{codeArray[0]}{codeArray[1]}");
        }


        private int[] ComputeCornerAndCell(double[] corners, double[] vs, int[] nums)
        {
            int codeX = 0;
            for (double i = corners[0]; i < corners[0] + nums[0] * vs[0]; i += vs[0])
            {
                int codeY = 0;
                for (double j = corners[1]; j < corners[1] + nums[1] * vs[1]; j += vs[1])
                {
                    bool min = this.Coordinate[0] > i && this.Coordinate[1] > j;
                    bool max = this.Coordinate[0] < i + vs[0] && this.Coordinate[1] < j + vs[1];

                    if (min && max)
                    {
                        // 将当前网格角点添加到 Corners 列表中
                        this.Corners.Add(new double[] { i, j });

                        // 计算最终的代码索引
                        int cornerX = corners[0] >= 0 ? codeX : nums[0] - 1 - codeX;
                        int cornerY = corners[1] >= 0 ? codeY : nums[1] - 1 - codeY;

                        return new int[] { cornerX, cornerY };
                    }

                    codeY++;
                }
                codeX++;
            }

            throw new InvalidOperationException("无法找到给定坐标的有效网格.");
        }
    }

    public class Coordinate
    {
        public double Lng { get; set; }

        public double Lat { get; set; }

        public double? Alt { get; set; }

        public Coordinate() { }

        public Coordinate(double lon, double lat, double? alt = null)
        {
            this.Lat = lat;
            this.Lng = lon;
            this.Alt = alt;
        }
    }
}